--[[
CastYeller2 - Announce important spell events.

Copyright (C) 2009 Adirelle

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--]]

if not CastYeller2 then return end
local CastYeller2 = CastYeller2 

--------------------------------------------------------------------------------
-- Locals
--------------------------------------------------------------------------------

local baseModulePrototype, sourceModulePrototype, sinkModulePrototype
local combatCallbacks

--------------------------------------------------------------------------------
-- Base module prototype
--------------------------------------------------------------------------------

baseModulePrototype = { 	
	core = CastYeller2,
	RegisterSignal = CastYeller2.RegisterSignal, 
	UnregisterSignal = CastYeller2.UnregisterSignal,
	UnregisterAllSignals = CastYeller2.UnregisterAllSignals,
	SendSignal = CastYeller2.SendSignal,
}

function baseModulePrototype:OnInitialize()
	CastYeller2:RegisterModule(self)
end

function baseModulePrototype:OnEnable()
end

function baseModulePrototype:OnDisable()
end

CastYeller2:SetDefaultModuleState(true)
CastYeller2:SetDefaultModulePrototype(baseModulePrototype)

--------------------------------------------------------------------------------
-- Source module prototype
--------------------------------------------------------------------------------

sourceModulePrototype = setmetatable({ moduleType = 'source' }, { __index = baseModulePrototype })
CastYeller2.sourceModulePrototype = sourceModulePrototype

function sourceModulePrototype:OnDisable()
	baseModulePrototype.OnDisable(self)
	self:UnregisterAllCombatListeners()
end

function sourceModulePrototype:FindAnnounceFor(trigger, spell)
	local inCombat = InCombatLockdown()
	for name, announce in self.core:IterateActiveAnnounces() do
		if announce.sources[self.moduleName] and announce.triggers[trigger] and announce.names[spell] and (inCombat or not announce.inCombat) then
			return announce
		end
	end
end

function sourceModulePrototype:IsNameWatched(name) 
	return self.core:IsNameWatched(name, self.moduleName) 
end
	
function sourceModulePrototype:IterateWatchedNames()
	return self.core:IterateWatchedNames(self.moduleName) 
end

function sourceModulePrototype:GetMessageFormat(announce, info)
	local trigger, target = info.trigger, info.target
	local custom
	if announce.custom[trigger] and announce.custom[trigger].enabled then
		local announces = announce.custom[trigger].announces
		if type(announces) == "table" then
			if #announces > 1 then
				custom = announces[math.random(1, #announces)]
			else
				custom = announces[1]
			end
		end
	end
	return custom or self.core.db.profile.templates[self.moduleName][trigger]
end

do
	local icons = {}
	for i = 1,8 do icons[_G['COMBATLOG_OBJECT_RAIDTARGET'..i]] = i end
	function sourceModulePrototype:GetRaidIcon(flags)
		return icons[bit.band(tonumber(flags) or 0, COMBATLOG_OBJECT_RAIDTARGET_MASK)]
	end
end

function sourceModulePrototype:TriggerAnnounce(trigger, target, spell, ...)	
	if not self:IsNameWatched(spell) then return end
	local announce = self:FindAnnounceFor(trigger, spell)
	if announce then
		local info = self:BuildInfo(announce, trigger, target, spell, ...)
		if info.raidIcon then
			info.raidIconTexture = '{rt'.. info.raidIcon..'}'
		end
		local messageFormat = self:GetMessageFormat(announce, info)
		info.messageFormat = messageFormat
		info.message = messageFormat and self.core:FormatMessage(messageFormat, info)
		self:SendSignal('OnAnnounceSent', announce, info)
	end
end

do 
	local info = {}
	function sourceModulePrototype:BuildInfo(announce, trigger, target, spell, duration, spellId, targetGUID, targetFlags, ...)
		wipe(info)
		info.trigger = trigger
		info.target = target
		if duration then
			local fmt, t = SecondsToTimeAbbrev(duration)
			info.duration = fmt:format(t)
		end
		info.spell = spell
		info.spellId = spellId
		info.targetGUID = targetGUID
		info.targetFlags = targetFlags
		for i = 1, select('#', ...), 2 do
			info[select(i, ...)] = select(i+1, ...)
		end
		if info.raidIcon == nil then			
			if targetFlags then
				info.raidIcon = self:GetRaidIcon(targetFlags)		
			elseif target and (UnitInParty(target) or UnitInRaid(target)) then
				info.raidIcon = GetRaidTargetIndex(target)					
			end
		end
		return info
	end
end

--------------------------------------------------------------------------------
-- Sink module prototype
--------------------------------------------------------------------------------

sinkModulePrototype = setmetatable({ moduleType = 'sink' }, { __index = baseModulePrototype })
CastYeller2.sinkModulePrototype = sinkModulePrototype

function sinkModulePrototype:OnEnable()
	baseModulePrototype.OnEnable(self)
	self:RegisterSignal("OnAnnounceSent")
end

function sinkModulePrototype:OnDisable()
	baseModulePrototype.OnDisable(self)
	self:UnregisterSignal("OnAnnounceSent")
end

function sinkModulePrototype:OnAnnounceSent(event, announce, message, target, spell)
	if announce.sinks[self.moduleName] then
		self:Sink(announce, message, target, spell)
	end
end

--------------------------------------------------------------------------------
-- Combat listener handling
--------------------------------------------------------------------------------

local registeredEvents = {}
combatCallbacks = LibStub("CallbackHandler-1.0"):New(
	sourceModulePrototype, 
  "RegisterCombatListener",
  "UnregisterCombatListener",
  "UnregisterAllCombatListeners"
)
LibStub('AceEvent-3.0'):Embed(combatCallbacks)

function combatCallbacks:OnUsed(target, event)
	if not next(registeredEvents) then		
		self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	end
	registeredEvents[event] = true
end

function combatCallbacks:OnUnused(target, event)
	registeredEvents[event] = nil
	if not next(registeredEvents) then
		self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	end
end

function combatCallbacks:COMBAT_LOG_EVENT_UNFILTERED(_, time, event, ...)
	if registeredEvents[event] then
		self:Fire(event, time, ...)
	end
end
